/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */
#pragma once

#define CACHE_INVERT_LOCAL_MATRIX

#include <animate/Node.h>
#include <lib3d/Math_lib3d.h>
#include <Atom.h>
#include <WowPtr.h>
#include <list>
#include <xml/Object.h>

namespace liba {
namespace lib3d {
namespace node {

class NodeBase 
	: public animate::Node
	, public xml::ObjectANSI
{
public:
	//Attributes
	static const Atom scale_name;
	static const Atom rotation_name;
	static const Atom position_name;

	explicit NodeBase( const Atom & name );
	virtual ~NodeBase();

	const Matrix & GetLocalMatrix()const;

#if defined( CACHE_INVERT_LOCAL_MATRIX )
	const Matrix& GetInvertLocalMatrix() const;
#else
	Matrix GetInvertLocalMatrix() const;
#endif

	const Vector & GetPosition()const;
	void ChangePosition( const Vector & pos );

	const Vector & GetRotation()const;
	void ChangeRotation( const Vector & rot );

	const Vector & GetScale()const;
	void ChangeScale( const Vector & scale );

	void InvalidateLocalMatrix() 
	{ 
		this->localMatrixDirty = true;
#if defined( CACHE_INVERT_LOCAL_MATRIX )
		this->invertLocalMatrixDirty = true;
#endif
		this->OnLocalMatrixInvalidated();
	}

	template<typename T>
	T* SafeDowncast()
	{
		if (this->GetClassName() == T::StaticGetClassName())
			return static_cast<T*>(this);

		return 0;
	}
protected:
	virtual void OnLocalMatrixInvalidated() { }
	virtual bool on_attribute( xml::Provider * prov, const std::string & name, const std::string & value );
	virtual void on_save_attributes( xml::SaverBase * saver )const;

	// Downcast support
	virtual liba::Atom GetClassName() = 0;

	mutable bool localMatrixDirty;
	mutable Matrix localMatrix;

#if defined( CACHE_INVERT_LOCAL_MATRIX )
	mutable Matrix invertLocalMatrix;
	mutable bool invertLocalMatrixDirty;
#endif

	properties::DependencyProperty<Vector> position;
	properties::DependencyProperty<Vector> rotation;
	properties::DependencyProperty<Vector> scale;

	void set_matrix( const Matrix & mat );
};


#define NODE_DEFINITION(type) \
public: \
	static Atom StaticGetClassName() \
	{ \
		static const Atom result(#type);\
		return result; \
	} \
private: \
	virtual liba::Atom GetClassName() { return StaticGetClassName(); }

inline const Vector & NodeBase::GetPosition()const
{
	return position;
}

inline void NodeBase::ChangePosition( const Vector & pos )
{
	if(pos == position.Get())
		return;
	
	position = pos;
	InvalidateLocalMatrix();
}

inline const Vector & NodeBase::GetRotation()const
{
	return rotation;
}

inline void NodeBase::ChangeRotation( const Vector & rot )
{
	if(rot == rotation.Get())
		return;
	
	rotation = rot;
	InvalidateLocalMatrix();
}

inline const Vector & NodeBase::GetScale()const
{
	return scale;
}

inline void NodeBase::ChangeScale( const Vector & scale )
{
	if(scale == this->scale.Get())
		return;
	
	this->scale = scale;
	InvalidateLocalMatrix();
}

inline const Matrix & NodeBase::GetLocalMatrix()const
{
	if( localMatrixDirty )
	{
		localMatrix = Matrix::translate( position );
		localMatrix *= Matrix::rotate( rotation );
		localMatrix *= Matrix::scale( scale );
		localMatrixDirty = false;
	}
	return localMatrix;
}

#if defined( CACHE_INVERT_LOCAL_MATRIX )
inline const Matrix& NodeBase::GetInvertLocalMatrix() const
{
	if ( !invertLocalMatrixDirty )
		return invertLocalMatrix;

	this->invertLocalMatrix = Matrix::invert( GetLocalMatrix() );
	this->invertLocalMatrixDirty = false;

	return this->invertLocalMatrix;
}
#else
inline Matrix NodeBase::GetInvertLocalMatrix() const
{
	return Matrix::invert( GetLocalMatrix() );
}
#endif

} // namespace node
} // namespace lib3d
} // namespace liba
using namespace liba;
